use std::io;
use std::process::exit;

fn main() {
    let mut task = TodoList::new();
    loop {
        println!("输入命令：show,add,del,done,undone,exit");
        let mut input: String = String::new();
        io::stdin().read_line(&mut input).expect("TODO: panic message");
        let mut args = input.trim().splitn(2, ' ');
        let command = args.next().unwrap_or("");
        let arg = args.next().unwrap_or("");
        match command {
            "show" => {
                task.show();
            }
            "add" => {
                task.add(task.next_id(), arg.to_string())
            }
            "del" => {
                task.del(arg.parse::<i32>().unwrap());
            }
            "done" => {
                task.done(arg.parse::<i32>().unwrap().abs());
            }
            "undone" => {
                task.undone(arg.parse::<i32>().unwrap().abs());
            }
            "exit" => {
                println!("bye!");
                exit(0);
            }
            _ => {}
        }
    }
}

#[derive(Debug)]
pub struct Todo {
    id: i32,
    text: String,
    done: bool,
}

#[derive(Debug)]
pub struct TodoList {
    pub todos: Vec<Todo>,
}

impl Todo {
    fn new(id: i32, text: String) -> Todo {
        Todo {
            id,
            text,
            done: false,
        }
    }
}


impl TodoList {
    fn new() -> Self {
        TodoList {
            todos: Vec::new()
        }
    }
    fn next_id(self) -> i32 {
        (self.todos.len() + 1) as i32
    }
    fn add(&mut self, id: i32, text: String) {
        self.todos.push(Todo::new(id, text));
    }

    fn del(&mut self, id: i32) {
        if let Some(t) = self.todos.iter().position(|x| { x.id == id }) {
            self.todos.remove(t);
        }
    }

    fn show(&self) {
        if self.todos.is_empty() {
            println!("list is empty!");
        } else {
            for t in &self.todos {
                println!("{} {} {}", t.id, if t.done { "✅" } else { "[ ]" }, t.text);
            }
        }
    }

    fn update(&mut self, id: i32, done: bool, _text: Option<String>) {
        if let Some(t) = self.todos
            .iter_mut()
            .find(|t| { t.id == id }) {
            t.done = done;
            if let Some(text) = _text {
                t.text = text;
            }
        }
    }
    fn done(&mut self, id: i32) {
        self.update(id, true, None);
    }
    fn undone(&mut self, id: i32) {
        self.update(id, false, None);
    }
}

#[cfg(test)]
mod tests {
    use crate::TodoList;

    #[test]
    fn add() {
        let mut list = TodoList::default();
        list.add(1, "done rust exercise p03".to_string());
        assert_eq!(1, list.list.len());
    }

    #[test]
    fn del() {
        let mut list = TodoList::default();
        list.add(1, "task1".to_string());
        list.add(2, "task2".to_string());
        list.add(3, "task3".to_string());

        list.del(2);
        assert_eq!(2, list.list.len());
        assert_eq!(1, list.list[0].id);
        assert_eq!(list.list[1].id, 3);
    }
}
